package com.vinjogames.util;

import java.util.LinkedList;
import java.util.List;

import android.graphics.PointF;

public class AILogic {
	
	private class ShotHistory {
		
		public PointF firedFrom;
		public PointF landedAt;
		public float power;
		public float wind;
		public float angle;
		public float enemyDamageDealt;
		public float friendlyDamageDealt;
		public boolean shotWasShort;
		public boolean facingWasRight;
	
		public ShotHistory(PointF fired, PointF landed, float pow, float wnd,
				float ang, float dmg, float ffDmg, boolean right, PointF enemyLoc) {
			firedFrom = fired; landedAt = landed; power = pow; wind = wnd;
			angle = ang; enemyDamageDealt = dmg; friendlyDamageDealt = ffDmg;
			facingWasRight = right; determineShortOrLong(enemyLoc);
		}
		
		private void determineShortOrLong(PointF p) {
			if(!facingWasRight) {
				shotWasShort = (p.x - landedAt.x) > 0 ? true : false;
			}
			else
				shotWasShort = (p.x - landedAt.x > 0) ? false : true;
		}
	}
	
	public class DesiredShot {
		public float power;
		public float angle;
		public boolean faceRight;
	}
	
	private List<ShotHistory> trackedShotHistory;
	private int shotsToStore;
	private boolean trackingEnemyShots;
	
	public AILogic(int MODE) {
		trackedShotHistory = new LinkedList<ShotHistory>();
		switch(MODE) {
		case GameConVars.AI_BASIC:
			shotsToStore = GameConVars.AI_BASIC_SHOT_STORAGE;
			trackingEnemyShots = false;
			break;
		case GameConVars.AI_MEDIUM:
			shotsToStore = GameConVars.AI_MEDIUM_SHOT_STORAGE;
			trackingEnemyShots = true;
			break;
		case GameConVars.AI_HARD:
			shotsToStore = GameConVars.AI_HARD_SHOT_STORAGE;
			trackingEnemyShots = true;
			break;
		}
	}	
	
	/**
	 * Log the most recent shot and remove the oldest if necessary. This takes in a lot of data, the
	 * ang is the angle of fire, dmg is total damage done to opponents, 
	 */
	public void logShot(PointF fired, PointF landed, float pow, float ang,
			float wnd, float dmg, float ffDmg, boolean right, PointF nearestEnemy) {
		ShotHistory newShot = new ShotHistory(fired, landed, pow, wnd, ang, dmg, ffDmg, right, nearestEnemy);
		trackedShotHistory.add(newShot);
		if(trackedShotHistory.size() > shotsToStore) trackedShotHistory.remove(0);
	}
	
	
	/**
	 * 
	 * Calculate the desired target, shot power, angle, and direction.
	 * Return shot power, angle, and direction.
	 * 
	 * @param shootingFrom Location of the tank firing.
	 * @param enemyTargets List of all enemies on the map.
	 * @return A shot or null if this tank should not shoot (rare).
	 */
	public DesiredShot calculateShot(PointF shootingFrom, List<PointF> enemyTargets) {
		DesiredShot shot = new DesiredShot();
		Logger.log("Shooting From: " + shootingFrom.x + " | " + shootingFrom.y);
		if(trackedShotHistory.isEmpty()) {
			shot.angle = 45.0f; //Shoot 50% angle and power towards the enemy and pray it reaches.
			shot.power = 7.5f;
			shot.faceRight = enemyTargets.get(0).x < shootingFrom.x ? false : true;
		}
		else /*if(trackedShotHistory.size() == 1)*/ {
			ShotHistory trackedShot = trackedShotHistory.remove(0);//trackedShotHistory.get(0);
			shot.faceRight = trackedShot.facingWasRight;
			PointF target = findTarget(enemyTargets, trackedShot.landedAt);
			
			if(target == null) return null;

			if(trackedShot.landedAt.x < target.x) {
				//Last shot was an overshot
				if(!shot.faceRight) {
					//Reduce angle & power by random factor.
					shot.angle = trackedShot.angle - (float)(GameConVars.AI_ANGLE_CORRECTION * Math.random());
					shot.power = trackedShot.power - (float)(GameConVars.AI_POWER_CORRECTION * Math.random());
				}
				//Undershot
				else {
					shot.angle = trackedShot.angle + (float)(GameConVars.AI_ANGLE_CORRECTION * Math.random());
					shot.power = trackedShot.power + (float)(GameConVars.AI_POWER_CORRECTION * Math.random());
				}
			}
			else {
				//Last shot was an overshot
				if(shot.faceRight) {
					//Reduce angle & power by random factor.
					shot.angle = trackedShot.angle - (float)(GameConVars.AI_ANGLE_CORRECTION * Math.random());
					shot.power = trackedShot.power - (float)(GameConVars.AI_POWER_CORRECTION * Math.random());
				}
				//Undershot
				else {
					shot.angle = trackedShot.angle + (float)(GameConVars.AI_ANGLE_CORRECTION * Math.random());
					shot.power = trackedShot.power + (float)(GameConVars.AI_POWER_CORRECTION * Math.random());
				}
			}			
		}
		
		return shot;
	}
	
	private PointF findTarget(List<PointF> targets, PointF lastHit) {
		
		if(targets.isEmpty()) return null;
		
		PointF desiredDest = targets.get(0);
		for(PointF p : targets) {
			if(Math.abs(lastHit.x - p.x) < Math.abs(lastHit.x - desiredDest.x))
				desiredDest = p;
		}
		
		return new PointF(desiredDest.x, desiredDest.y);
	}
}
